﻿using System;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Microsoft.SharePoint.Client.Services;
using System.Web;

namespace DeGiglio.biz.Twitter.Services
{
    // Inspiration for Service Host Factory code comes from here: http://www.informationworker.co.za/blogs/mirror/Lists/Posts/Post.aspx?ID=1377

    /// <summary>
    /// The SharePoint WCF factories are great for simply deploying services to your SharePoint farm.
    /// This custom factory is used to extend one of those factories so that we can have proper
    /// error handling between our jQuery client and the WCF service.
    /// </summary>
    public class TwitterServiceHostFactory : MultipleBaseAddressDataServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new TwitterServiceHost(serviceType, baseAddresses);
        }
    }

    public class TwitterServiceHost : MultipleBaseAddressDataServiceHost
    {
        public TwitterServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
        }

        /// <summary>
        /// In the OnOpening method we add the behviors for our custom error handler and the HttpBehavior which is needed for jQuery
        /// </summary>
        protected override void OnOpening()
        {
            base.OnOpening();

            foreach (ServiceEndpoint endpoint in base.Description.Endpoints)
            {
                if (((endpoint.Binding != null)
                    && (endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() != null))
                    && (endpoint.Behaviors.Find<WebScriptEnablingBehavior>() == null))
                {
                    // try remove any previous behaviours
                    while (endpoint.Behaviors.Count > 0)
                    {
                        endpoint.Behaviors.RemoveAt(0);
                    }
                    endpoint.Behaviors.Add(new WebHttpBehavior());
                    endpoint.Behaviors.Add(new TwitterServiceBehavior());
                }

                ServiceDebugBehavior debug = this.Description.Behaviors.Find<ServiceDebugBehavior>();

                // if not found - add behavior with setting turned on  
                if (debug == null)
                {
                    this.Description.Behaviors.Add(new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
                }

                else
                {
                    // make sure setting is turned ON     
                    if (!debug.IncludeExceptionDetailInFaults)
                    {
                        debug.IncludeExceptionDetailInFaults = true;
                    }
                }

                ServiceMetadataBehavior metadata = this.Description.Behaviors.Find<ServiceMetadataBehavior>();

                // if not found - add behavior with setting turned on  
                if (metadata == null)
                {
                    if (HttpContext.Current.Request.IsSecureConnection)
                    {
                        this.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpsGetEnabled = true });
                    }
                    else
                    {
                        this.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
                    }
                }
                else
                {
                    // make sure setting is turned ON     
                    if (!metadata.HttpGetEnabled)
                    {
                        if (HttpContext.Current.Request.IsSecureConnection)
                        {
                            metadata.HttpsGetEnabled = true;
                        }
                        else
                        {
                            metadata.HttpGetEnabled = true;
                        }
                    }
                }
            }
        }
    }

    // Inspiration for custom error handler comes from here: http://zamd.net/2008/07/08/error-handling-with-webhttpbinding-for-ajaxjson/
    public class TwitterServiceBehavior : IEndpointBehavior
    {
        public TwitterServiceBehavior() { }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            if (endpointDispatcher == null || endpointDispatcher.GetType() != typeof(EndpointDispatcher))
                return;

            endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();
            endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new TwitterServiceErrorHandler());
        }

        public void Validate(ServiceEndpoint endpoint) { }
    }

    public class TwitterServiceErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            return true;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            TwitterServiceError twitterError = new TwitterServiceError { Message = error.Message };

            fault = Message.CreateMessage(version, "", twitterError, new DataContractJsonSerializer(twitterError.GetType()));

            // tell WCF to use JSON encoding rather than default XML
            var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
            fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

            // return custom error code.
            var rmp = new HttpResponseMessageProperty();
            rmp.StatusCode = HttpStatusCode.BadRequest;
            rmp.Headers[HttpResponseHeader.ContentType] = "application/json";
            rmp.Headers.Add("jsonerror", "true");

            // put appropraite description here..
            rmp.StatusDescription = "See fault object for more information.";
            fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);
        }
    }

    #region " Custom Error Object "
    [DataContractFormat]
    public class TwitterServiceError
    {
        [DataMember]
        public string Message { get; set; }
    }
    #endregion
}
